#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
简化的字符检测模块 - 基于轮廓检测和基本特征
"""

import cv2
import numpy as np
import numpy.typing as npt
from typing import Tuple, List, Dict, Optional, Any
import logging
from dataclasses import dataclass
from enum import Enum
import math

class KeyErrorType(Enum):
    MISSING_CHAR = "缺失字符"
    WRONG_CHAR = "错误字符"
    EXTRA_CHAR = "多余字符"
    POSITION_ERROR = "位置错误"

@dataclass
class CharacterKey:
    """字符按键信息"""
    position: Tuple[int, int]  # 按键中心位置
    size: Tuple[int, int]      # 按键大小 (width, height)
    character: str             # 识别的字符
    confidence: float          # 识别置信度
    bounding_box: Tuple[int, int, int, int]  # 边界框 (x, y, w, h)

@dataclass
class KeyError:
    """按键错误信息"""
    error_type: KeyErrorType
    position: Tuple[int, int]
    expected_char: str
    actual_char: str
    confidence: float
    description: str

class SimpleCharacterDetector:
    def __init__(self):
        self.logger = logging.getLogger(__name__)
        
        # 字符检测参数
        self.min_char_area = 200        # 最小字符区域面积
        self.max_char_area = 5000       # 最大字符区域面积
        self.char_aspect_ratio_range = (0.5, 2.0)  # 字符长宽比范围
        
        # 检测阈值
        self.contour_area_threshold = 200
        self.min_contour_perimeter = 50
        
    def detect_keyboard_characters(self, image: npt.NDArray) -> List[CharacterKey]:
        """
        检测键盘上的字符 - 简化版本
        
        Args:
            image: 输入图像
            
        Returns:
            字符按键列表
        """
        self.logger.info("开始检测键盘字符...")
        
        # 预处理图像
        processed_image = self._preprocess_for_character_detection(image)
        
        # 检测字符区域
        char_regions = self._detect_character_regions(processed_image)
        
        # 识别字符
        character_keys = []
        for i, region in enumerate(char_regions):
            char_key = self._create_character_key(image, region, i)
            if char_key:
                character_keys.append(char_key)
        
        # 按位置排序
        character_keys.sort(key=lambda k: (k.position[1], k.position[0]))
        
        self.logger.info(f"检测到 {len(character_keys)} 个字符按键")
        return character_keys
    
    def _preprocess_for_character_detection(self, image: npt.NDArray) -> npt.NDArray:
        """为字符检测预处理图像"""
        # 转换为灰度图像
        if len(image.shape) == 3:
            gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
        else:
            gray = image.copy()
        
        # 高斯模糊减少噪声
        blurred = cv2.GaussianBlur(gray, (3, 3), 0)
        
        # 自适应阈值二值化
        binary = cv2.adaptiveThreshold(
            blurred, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2
        )
        
        # 形态学操作
        kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (2, 2))
        binary = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel)
        
        return binary
    
    def _detect_character_regions(self, binary_image: npt.NDArray) -> List[Tuple[int, int, int, int]]:
        """检测字符区域"""
        # 查找轮廓
        contours, _ = cv2.findContours(binary_image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        
        char_regions = []
        for contour in contours:
            area = cv2.contourArea(contour)
            perimeter = cv2.arcLength(contour, True)
            
            # 过滤面积和周长
            if (self.min_char_area < area < self.max_char_area and 
                perimeter > self.min_contour_perimeter):
                
                x, y, w, h = cv2.boundingRect(contour)
                
                # 过滤长宽比
                aspect_ratio = w / h
                if self.char_aspect_ratio_range[0] < aspect_ratio < self.char_aspect_ratio_range[1]:
                    char_regions.append((x, y, w, h))
        
        return char_regions
    
    def _create_character_key(self, original_image: npt.NDArray, 
                            region: Tuple[int, int, int, int], index: int) -> Optional[CharacterKey]:
        """创建字符按键对象"""
        x, y, w, h = region
        
        # 提取字符区域
        char_roi = original_image[y:y+h, x:x+w]
        
        if char_roi.size == 0:
            return None
        
        # 简单的字符识别 - 基于位置和大小
        character = self._simple_character_recognition(char_roi, index)
        
        # 计算置信度（基于区域特征）
        confidence = self._calculate_confidence(char_roi)
        
        return CharacterKey(
            position=(x + w//2, y + h//2),
            size=(w, h),
            character=character,
            confidence=confidence,
            bounding_box=(x, y, w, h)
        )
    
    def _simple_character_recognition(self, char_roi: npt.NDArray, index: int) -> str:
        """简单的字符识别"""
        # 基于索引生成字符标签
        # 这是一个简化的实现，实际应用中应该使用更复杂的OCR
        
        # 计算ROI的基本特征
        if len(char_roi.shape) == 3:
            gray = cv2.cvtColor(char_roi, cv2.COLOR_BGR2GRAY)
        else:
            gray = char_roi.copy()
        
        # 计算像素密度
        total_pixels = gray.size
        dark_pixels = np.sum(gray < 128)
        density = dark_pixels / total_pixels
        
        # 基于特征生成字符
        if density > 0.3:  # 高密度，可能是字母
            char_index = index % 26
            return chr(ord('A') + char_index)
        elif density > 0.1:  # 中等密度，可能是数字
            return str(index % 10)
        else:  # 低密度，可能是特殊字符
            special_chars = ['Space', 'Enter', 'Shift', 'Ctrl', 'Alt']
            return special_chars[index % len(special_chars)]
    
    def _calculate_confidence(self, char_roi: npt.NDArray) -> float:
        """计算识别置信度"""
        if len(char_roi.shape) == 3:
            gray = cv2.cvtColor(char_roi, cv2.COLOR_BGR2GRAY)
        else:
            gray = char_roi.copy()
        
        # 计算对比度
        contrast = np.std(gray)
        
        # 计算边缘强度
        edges = cv2.Canny(gray, 50, 150)
        edge_density = np.sum(edges > 0) / edges.size
        
        # 综合置信度
        confidence = min(0.9, (contrast / 50.0 + edge_density) / 2.0)
        return max(0.1, confidence)
    
    def compare_with_reference(self, current_keys: List[CharacterKey], 
                             reference_keys: List[CharacterKey]) -> List[KeyError]:
        """
        与参考键盘比较，检测错误
        
        Args:
            current_keys: 当前检测到的按键
            reference_keys: 参考键盘按键
            
        Returns:
            错误列表
        """
        self.logger.info("开始比较键盘字符...")
        
        errors = []
        
        # 创建参考按键映射（基于位置）
        ref_key_map = {}
        for key in reference_keys:
            # 使用位置作为键
            pos_key = (key.position[0] // 50, key.position[1] // 50)  # 50像素容差
            ref_key_map[pos_key] = key
        
        current_key_map = {}
        for key in current_keys:
            pos_key = (key.position[0] // 50, key.position[1] // 50)
            current_key_map[pos_key] = key
        
        # 检查缺失的按键
        for pos_key, ref_key in ref_key_map.items():
            if pos_key not in current_key_map:
                errors.append(KeyError(
                    error_type=KeyErrorType.MISSING_CHAR,
                    position=ref_key.position,
                    expected_char=ref_key.character,
                    actual_char="",
                    confidence=1.0,
                    description=f"缺失字符: {ref_key.character}"
                ))
        
        # 检查多余的按键
        for pos_key, curr_key in current_key_map.items():
            if pos_key not in ref_key_map:
                errors.append(KeyError(
                    error_type=KeyErrorType.EXTRA_CHAR,
                    position=curr_key.position,
                    expected_char="",
                    actual_char=curr_key.character,
                    confidence=1.0,
                    description=f"多余字符: {curr_key.character}"
                ))
        
        # 检查字符不匹配
        for pos_key in ref_key_map:
            if pos_key in current_key_map:
                ref_key = ref_key_map[pos_key]
                curr_key = current_key_map[pos_key]
                
                if ref_key.character != curr_key.character:
                    errors.append(KeyError(
                        error_type=KeyErrorType.WRONG_CHAR,
                        position=curr_key.position,
                        expected_char=ref_key.character,
                        actual_char=curr_key.character,
                        confidence=0.8,
                        description=f"字符错误: 期望 {ref_key.character}, 实际 {curr_key.character}"
                    ))
        
        self.logger.info(f"检测到 {len(errors)} 个字符错误")
        return errors
    
    def visualize_character_detection(self, image: npt.NDArray, 
                                    character_keys: List[CharacterKey],
                                    errors: List[KeyError]) -> npt.NDArray:
        """可视化字符检测结果"""
        vis_image = image.copy()
        
        # 绘制检测到的字符
        for key in character_keys:
            x, y, w, h = key.bounding_box
            
            # 绘制边界框
            cv2.rectangle(vis_image, (x, y), (x+w, y+h), (0, 255, 0), 2)
            
            # 绘制字符
            cv2.putText(vis_image, key.character, (x, y-5), 
                       cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
            
            # 绘制置信度
            cv2.putText(vis_image, f"{key.confidence:.2f}", (x, y+h+15), 
                       cv2.FONT_HERSHEY_SIMPLEX, 0.4, (255, 255, 255), 1)
        
        # 绘制错误标记
        for error in errors:
            x, y = error.position
            
            if error.error_type == KeyErrorType.MISSING_CHAR:
                color = (0, 0, 255)  # 红色
                symbol = "X"
            elif error.error_type == KeyErrorType.EXTRA_CHAR:
                color = (255, 0, 255)  # 紫色
                symbol = "+"
            elif error.error_type == KeyErrorType.WRONG_CHAR:
                color = (0, 255, 255)  # 黄色
                symbol = "!"
            else:
                color = (255, 255, 0)  # 青色
                symbol = "?"
            
            # 绘制错误标记
            cv2.circle(vis_image, (x, y), 20, color, 3)
            cv2.putText(vis_image, symbol, (x-8, y+8), 
                       cv2.FONT_HERSHEY_SIMPLEX, 0.8, color, 2)
            
            # 绘制错误描述
            cv2.putText(vis_image, error.description, (x+25, y), 
                       cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 1)
        
        return vis_image
